//=========================================================================
// Copyright (C) 2024 The C++ Component Model(COMO) Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//=========================================================================

/**
 * Debugging for pmalloc.
 */

#include <stdlib.h>
#include "pmfunction.h"
#include "pminternal.h"

#define MAGICWORD static_cast<unsigned int>(0xd4c2afe9)
#define MAGICWORDFREE static_cast<unsigned int>(0xff04feca)
#define MAGICBYTE (static_cast<char>(0xd7))

struct hdr {
    union {
        void *desc;
        char pad[8];
    } u;
    unsigned int size;
    unsigned int magic;
};

static void checkhdr(struct mdesc *mdp, struct hdr *hdr)
{
    if (hdr->magic == MAGICWORDFREE) {
        (*mdp->abortfunc)(reinterpret_cast<void *>(&hdr[1]), hdr->u.desc, -1);
    }
    else if (hdr->magic != MAGICWORD) {
        (*mdp->abortfunc)(reinterpret_cast<void *>(&hdr[1]), hdr->u.desc, 0);
    }
    else if (((char *)&hdr[1])[hdr->size] != MAGICBYTE) {
        (*mdp->abortfunc)(reinterpret_cast<void *>(&hdr[1]), hdr->u.desc, 1);
    }
}

void pmfree_check(void *md, void *ptr, void *desc)
{
    struct hdr *hdr = static_cast<struct hdr *>(ptr) - 1;
    struct mdesc *mdp = static_cast<struct mdesc *>(md);

    checkhdr(mdp, hdr);
    hdr->magic = MAGICWORDFREE;
    pmfree(md, static_cast<void *>(hdr));
}

void *pmalloc_check(void *md, const size_t size, void *desc)
{
    struct hdr *hdr;
    /*    struct mdesc *mdp = (struct mdesc *)md; */
    size_t nbytes;

    nbytes = sizeof(struct hdr) + size + 1;
    hdr = static_cast<struct hdr *>(pmalloc(md, nbytes));
    if (hdr != nullptr) {
        hdr->size = size;
        hdr->u.desc = desc;
        hdr->magic = MAGICWORD;
        hdr++;
        *(reinterpret_cast<char *>(hdr) + size) = MAGICBYTE;
    }
    return static_cast<void *>(hdr);
}

void *pmcalloc_check(void *md, const size_t num, const size_t size, void *desc)
{
    void *result;
    if ((result = pmalloc_check(md, num * size, desc)) != nullptr) {
        bzero(result, num * size);
    }
    return (result);
}

void *pmrealloc_check(void *md, void *ptr, const size_t size, void *desc)
{
    struct hdr *hdr;
    struct mdesc *mdp = static_cast<struct mdesc *>(md);
    size_t nbytes;

    if (nullptr == ptr) {
        return pmalloc_check(md, size, desc);
    }

    hdr = static_cast<struct hdr *>(ptr) - 1;
    checkhdr(mdp, hdr);

    nbytes = sizeof(struct hdr) + size + 1;
    hdr = static_cast<struct hdr *>(pmrealloc(md, (void *)hdr, nbytes));
    if (hdr != nullptr) {
        hdr->size = size;
        hdr->u.desc = desc;
        hdr++;
        *(reinterpret_cast<char *>(hdr) + size) = MAGICBYTE;
    }
    return static_cast<void *>(hdr);
}

static void default_abort(void *p, void *desc, int overflow)
{
    abort();
}

void pmcheck(void *md, void (*func)(void *, void *, int))
{
    struct mdesc *mdp = static_cast<struct mdesc *>(md);

    mdp->abortfunc = ((func != nullptr) ? func : default_abort);
    mdp->flags |= PMALLOC_PMCHECK_USED;
}

int check_heap_free_info(struct mdesc *md)
{
    int ret = 0;
    size_t start, current, next;
    struct mdesc *mdp = md;

    fprintf(stderr, "\n");

    start = current = MALLOC_SEARCH_START;
    do {
        next = mdp->mblkinfo[current].free.next;

        if (mdp->mblkinfo[next].free.prev != current) {
            fprintf(stderr, "CURRENT->NEXT->prev IS NOT CURRENT !!! \n");
            ret = -1;
            break;
        }

        if (next == start) {
            break;
        }

        current = next;

        fprintf(stderr, ".");
    } while (1);

    fprintf(stderr, "\n");
    return ret;
}
